package comm;
//Interpret the message types in data
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.security.Key;
import java.util.Arrays;
import java.util.List;

import store.*;
import utils.Convert;

public class MessageParser implements ServerListener
{
	
	private byte[] data;
	private List<storage> iptable; 
	private MsgAction action;
	private Cserver server;
	private String recvIP;
	private Convert conv;
	
	//Constants used to indicate message types
	private static final byte CMD_UPDATE			= 1;	//update the server's IP table
	private static final byte CMD_ACK_JOIN	        = 2;	//Acknowledge request from client	
	private static final byte CMD_ACK_REJECT        = 3;	//Reject join request from client
	private static final byte CMD_REQ_JOIN		    = 4;	//Send request for joining group
	private static final byte CMD_START_RECORD		= 5;	//start recording
	private static final byte CMD_STOP_RECORD      	= 6;	//stop recording
	private static final byte CMD_KEY		      	= 7;	//send key
	private static final byte CMD_REC_FILE	      	= 8;	//Contains the recorded file
	private static final byte CMD_TEST_MIX			= 11;	//Test the Mixing Capabilities
	
	public MessageParser()
	{
		startServer();
	}
	
	public MessageParser(byte[] incoming)
	{
		startServer();	//Start Server execution
		this.data = incoming;
	}
	
	//Start server thread
	public void startServer()
	{		
		server = new Cserver(5000);
		server.registerListener(this);
		new Thread(server).start();
	}
	
	public void addActionProvider(MsgAction ac)
	{
		this.action = ac;
	}
	
	//Store data and IP address received at the server
	public void serverNotify(byte[] data, String ip)
	{
		this.data = data;
		this.recvIP = ip;
		this.parsedata();
	}
	
	//Parse received data based on message types
	public void parsedata()
	{
		byte msgType = this.data[0];	//Message type is stored in data[0]
		this.data = Arrays.copyOfRange(this.data, 1, this.data.length);
		switch(msgType)
		{
			case MessageParser.CMD_UPDATE:
				this.parseUpdate();
				break;
			case MessageParser.CMD_ACK_JOIN:
				this.parseJoin();
				break;
			case MessageParser.CMD_ACK_REJECT:
				this.parseReject();
				break;
			case MessageParser.CMD_REQ_JOIN:
				this.parseRequestJoin();
				break;
			case MessageParser.CMD_START_RECORD:
				this.parseStartRecord();
				break;
			case MessageParser.CMD_STOP_RECORD:
				this.parseStopRecord();
				break;
			case MessageParser.CMD_KEY:
				this.parsekey();
				break;
			case MessageParser.CMD_REC_FILE:
				this.parseFile();
				break;
			case MessageParser.CMD_TEST_MIX:
				this.parseTestMix();
				break;
			default:
				break;
				
		}
	}
	
	private void parseTestMix()
	{
		// TODO Auto-generated method stub
			Key k = null;
			try {
			    ByteArrayInputStream bis = new ByteArrayInputStream (this.data);	
			    ObjectInputStream ois = new ObjectInputStream (bis);
			    k = (Key) ois.readObject();		//read the serialized object
			  }
			  catch (IOException ex) {
			    //TODO: Handle the exception
				  System.out.println("Error in MessageParser:" + ex.toString());
			  }
			  catch (ClassNotFoundException ex) {
			    //TODO: Handle the exception
				  System.out.println("Error in MessageParser:" + ex.toString());
			  }
			
			this.action.testMix(k, this.recvIP);
	}
	
	//Store recorded contents to a file
	private void parseFile() 
	{
		// TODO Auto-generated method stub
		String filename = recvIP + ".au";
		File inFile = new File(filename);	//Create a file to save recorded data
		if(inFile.exists())
		{
			inFile.delete();
			inFile = new File(filename);
		}
		
		
		try {
			inFile.createNewFile();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
		
        try {
        	FileOutputStream fos = new FileOutputStream(inFile);
            fos.write(this.data);	//Write recorded data using FileOutputStream
        	fos.flush();
        	fos.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
        
		this.action.recieveFile(inFile, this.recvIP);
		
	}

	//Interpret the key that is used for encryption and decryption
	private void parsekey() {
		// TODO Auto-generated method stub
		Key k = null;
		try {
		    ByteArrayInputStream bis = new ByteArrayInputStream (this.data);	
		    ObjectInputStream ois = new ObjectInputStream (bis);
		    k = (Key) ois.readObject();		//read the serialized object
		  }
		  catch (IOException ex) {
		    //TODO: Handle the exception
			  System.out.println("Error in MessageParser:" + ex.toString());
		  }
		  catch (ClassNotFoundException ex) {
		    //TODO: Handle the exception
			  System.out.println("Error in MessageParser:" + ex.toString());
		  }
		
		this.action.recieveKey(k, this.recvIP);	
	}

	//Stop the recording process
	private void parseStopRecord() {
		// TODO Auto-generated method stub
		this.action.stopRecord(this.recvIP);
		
	}

	//Start recording
	private void parseStartRecord() {
		// TODO Auto-generated method stub
		this.action.startRecord(this.recvIP);
	}

	//Parse the request from client to join the group
	private void parseRequestJoin() {
		// TODO Auto-generated method stub
		conv = new Convert();
		String recvPin = (String) conv.toObject(this.data);
		this.action.requestJoin(recvPin, this.recvIP);
	}

	//Parse the reject message from the server when a client can't join the group
	private void parseReject() {
		// TODO Auto-generated method stub
		this.action.joinRejected(this.recvIP);
	}

	//Parse the data that have to be updated in the IP table at the server
	@SuppressWarnings("unchecked")
	private void parseUpdate()
	{
		try 
		{
		    ByteArrayInputStream bis = new ByteArrayInputStream(this.data,0,this.data.length);
		    ObjectInputStream ois = new ObjectInputStream (bis);
		    iptable = (List<storage>) ois.readObject();	//Read the object into iptable
		    this.action.updateClientTable(iptable, this.recvIP);	//Update the client details at the IP table
		} 
		catch(Exception e)
		{
			//Exception code
		}
	}
	
	//Parse the join request from the client
	private void parseJoin()
	{
		this.action.joinAccepted(this.recvIP);
	}
	
	
}
